package com.beauty.product.service.impl;

import com.beauty.product.document.ProductAttributeDocument;
import com.beauty.product.document.ProductDocument;
import com.beauty.product.dto.PageResponse;
import com.beauty.product.dto.ProductDto;
import com.beauty.product.entity.Product;
import com.beauty.product.entity.ProductAttribute;
import com.beauty.product.entity.ProductImage;
import com.beauty.product.repository.ProductAttributeRepository;
import com.beauty.product.repository.ProductImageRepository;
import com.beauty.product.repository.ProductRepository;
import com.beauty.product.repository.es.ProductDocumentRepository;
import com.beauty.product.service.ProductElasticsearchService;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

@Slf4j
@Service
@RequiredArgsConstructor
public class ProductElasticsearchServiceImpl implements ProductElasticsearchService {

    private final ProductRepository productRepository;
    private final ProductAttributeRepository attributeRepository;
    private final ProductImageRepository imageRepository;
    private final ProductDocumentRepository productDocumentRepository;
    private final ElasticsearchOperations elasticsearchOperations;
    private final ElasticsearchClient elasticsearchClient;

    @Override
    public ProductDocument save(ProductDocument productDocument) {
        return productDocumentRepository.save(productDocument);
    }

    @Override
    public Iterable<ProductDocument> saveAll(List<ProductDocument> productDocuments) {
        return productDocumentRepository.saveAll(productDocuments);
    }

    @Override
    public ProductDocument findById(String id) {
        return productDocumentRepository.findById(id).orElse(null);
    }

    @Override
    public PageResponse<ProductDto> search(String keyword, Pageable pageable) {
        Page<ProductDocument> searchResult;

        if (StringUtils.hasText(keyword)) {
            searchResult = productDocumentRepository.searchByKeyword(keyword, pageable);
        } else {
            searchResult = productDocumentRepository.findAll(pageable);
        }

        Page<ProductDto> productDtoPage = searchResult.map(this::convertToProductDto);
        return PageResponse.fromPage(productDtoPage);
    }

    @Override
    public PageResponse<ProductDto> advancedSearch(String name, String brand, String description,
                                                   BigDecimal minPrice, BigDecimal maxPrice,
                                                   Long categoryId, Boolean isPublished,
                                                   Boolean isRecommended, Boolean isNew, Boolean isHot,
                                                   Pageable pageable) {

        CriteriaQuery query = new CriteriaQuery(new Criteria());

        if (StringUtils.hasText(name)) {
            query.addCriteria(new Criteria("name").contains(name));
        }

        if (StringUtils.hasText(brand)) {
            query.addCriteria(new Criteria("brand").contains(brand));
        }

        if (StringUtils.hasText(description)) {
            query.addCriteria(new Criteria("description").contains(description));
        }

        if (minPrice != null && maxPrice != null) {
            query.addCriteria(new Criteria("price").between(minPrice.toString(), maxPrice.toString()));
        } else if (minPrice != null) {
            query.addCriteria(new Criteria("price").greaterThanEqual(minPrice.toString()));
        } else if (maxPrice != null) {
            query.addCriteria(new Criteria("price").lessThanEqual(maxPrice.toString()));
        }

        if (categoryId != null) {
            query.addCriteria(new Criteria("categoryId").is(categoryId));
        }

        if (isPublished != null) {
            query.addCriteria(new Criteria("isPublished").is(isPublished));
        }

        if (isRecommended != null) {
            query.addCriteria(new Criteria("isRecommended").is(isRecommended));
        }

        if (isNew != null) {
            query.addCriteria(new Criteria("isNew").is(isNew));
        }

        if (isHot != null) {
            query.addCriteria(new Criteria("isHot").is(isHot));
        }

        query.setPageable(pageable);

        SearchHits<ProductDocument> searchHits = elasticsearchOperations.search(query, ProductDocument.class);
        List<ProductDto> productDtos = searchHits.stream()
                .map(SearchHit::getContent)
                .map(this::convertToProductDto)
                .collect(Collectors.toList());

        long total = searchHits.getTotalHits();
        Page<ProductDto> page = PageResponse.createPage(productDtos, pageable, total);

        return PageResponse.fromPage(page);
    }

    @Override
    public void deleteById(String id) {
        productDocumentRepository.deleteById(id);
    }

    @Override
    @Transactional(readOnly = true)
    public void syncProductFromDB(String productId) {
        Optional<Product> productOptional = productRepository.findById(productId);
        if (productOptional.isPresent()) {
            Product product = productOptional.get();
            ProductDocument productDocument = convertToProductDocument(product);
            productDocumentRepository.save(productDocument);
            log.info("Product with ID: {} synced to Elasticsearch", productId);
        } else {
            log.warn("Product with ID: {} not found in database", productId);
        }
    }

    @Override
    @Transactional(readOnly = true)
    public void syncAllProductsFromDB() {
        Iterable<Product> products = productRepository.findAll();
        List<ProductDocument> productDocuments = StreamSupport.stream(products.spliterator(), false)
                .map(this::convertToProductDocument)
                .collect(Collectors.toList());

        productDocumentRepository.saveAll(productDocuments);
        log.info("All products synced to Elasticsearch, count: {}", productDocuments.size());
    }

    private ProductDocument convertToProductDocument(Product product) {
        ProductDocument document = new ProductDocument();
        document.setId(product.getId());
        document.setName(product.getName());
        document.setSku(product.getSku());
        document.setPrice(product.getPrice() != null ? product.getPrice().toString() : null);
        document.setOriginalPrice(product.getOriginalPrice() != null ? product.getOriginalPrice().toString() : null);
        document.setCategoryId(product.getCategoryId());
        document.setBrand(product.getBrand());
        document.setDescription(product.getDescription());
        
        // 对于coverImage字段，如果是base64数据，只保存URL或路径部分，或设置为null
        String coverImage = product.getCoverImage();
        if (coverImage != null && coverImage.startsWith("data:image")) {
            // 这是base64数据，不适合存储在Elasticsearch中
            // 可以选择在这里设置为null，或者存储一个占位符
            document.setCoverImage("image_in_base64_format"); // 或者 document.setCoverImage(null);
        } else {
            // 正常的URL或路径
            document.setCoverImage(coverImage);
        }
        
        document.setIsPublished(product.getIsPublished());
        document.setIsRecommended(product.getIsRecommended());
        document.setIsNew(product.getIsNew());
        document.setIsHot(product.getIsHot());
        document.setSort(product.getSort());
        document.setSales(product.getSales());
        document.setCreatedTime(product.getCreatedTime() != null ? product.getCreatedTime().toLocalDate() : null);
        document.setUpdatedTime(product.getUpdatedTime() != null ? product.getUpdatedTime().toLocalDate() : null);

        List<ProductAttribute> attributes = attributeRepository.findByProductId(product.getId());
        if (attributes != null && !attributes.isEmpty()) {
            List<ProductAttributeDocument> attributeDocuments = attributes.stream()
                    .map(attr -> {
                        ProductAttributeDocument attrDoc = new ProductAttributeDocument();
                        attrDoc.setName(attr.getName());
                        attrDoc.setValue(attr.getValue());
                        attrDoc.setSort(attr.getSort());
                        attrDoc.setIsSearchable(attr.getIsSearchable());
                        attrDoc.setIsVisible(attr.getIsVisible());
                        return attrDoc;
                    })
                    .collect(Collectors.toList());
            document.setAttributes(attributeDocuments);
        }

        List<ProductImage> images = imageRepository.findByProductId(product.getId());
        if (images != null && !images.isEmpty()) {
            List<String> imageUrls = images.stream()
                    .map(ProductImage::getImageUrl)
                    .collect(Collectors.toList());
            document.setImageUrls(imageUrls);
        }

        return document;
    }

    private ProductDto convertToProductDto(ProductDocument document) {
        ProductDto dto = new ProductDto();
        dto.setId(document.getId());
        dto.setName(document.getName());
        dto.setSku(document.getSku());
        dto.setPrice(document.getPrice() != null ? new BigDecimal(document.getPrice()) : null);
        dto.setOriginalPrice(document.getOriginalPrice() != null ? new BigDecimal(document.getOriginalPrice()) : null);
        dto.setCategoryId(document.getCategoryId());
        dto.setBrand(document.getBrand());
        dto.setDescription(document.getDescription());
        dto.setCoverImage(document.getCoverImage());
        dto.setIsPublished(document.getIsPublished());
        dto.setIsRecommended(document.getIsRecommended());
        dto.setIsNew(document.getIsNew());
        dto.setIsHot(document.getIsHot());
        dto.setSort(document.getSort());
        dto.setSales(document.getSales());
        dto.setCreatedTime(document.getCreatedTime() != null ? document.getCreatedTime().atStartOfDay() : null);
        dto.setUpdatedTime(document.getUpdatedTime() != null ? document.getUpdatedTime().atStartOfDay() : null);

        return dto;
    }
}
